package cn.dataling.ai.util;

import cn.dataling.ai.common.ExceptionCore;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.ObjectUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * 本地缓存
 */
public class LocalCache {

    private static final Map<String, Data> CACHE = new ConcurrentHashMap<>();

    /**
     * 存值
     *
     * @param key   key
     * @param value value
     */
    public static void set(String key, Object value) {
        CACHE.put(key, new Data(value));
    }

    /**
     * 存值
     *
     * @param key      key
     * @param supplier 函数，调用方需要传递一个带有返回值的函数，这里将会缓存返回值
     */
    public static void set(String key, Supplier<?> supplier) {
        set(key, supplier.get());
    }

    /**
     * 存值
     *
     * @param key     key
     * @param value   value
     * @param expired 过期时间
     */
    public static void set(String key, Object value, Long expired, TimeUnit timeUnit) {
        switch (timeUnit) {
            case MILLISECONDS:
                CACHE.put(key, new Data(System.currentTimeMillis() + expired, value));
                break;
            case SECONDS:
                CACHE.put(key, new Data(System.currentTimeMillis() + expired * 1000, value));
                break;
            case MINUTES:
                CACHE.put(key, new Data(System.currentTimeMillis() + expired * 1000 * 60, value));
                break;
            case HOURS:
                CACHE.put(key, new Data(System.currentTimeMillis() + expired * 1000 * 60 * 60, value));
                break;
            case DAYS:
                CACHE.put(key, new Data(System.currentTimeMillis() + expired * 1000 * 60 * 60 * 24, value));
                break;
            default:
                throw new ExceptionCore("不支持该时间单位");
        }
    }

    /**
     * 存值
     *
     * @param key      key
     * @param supplier 函数，调用方需要传递一个带有返回值的函数，这里将会缓存返回值
     * @param expired  过期时间
     */
    public static void set(String key, Supplier<?> supplier, Long expired, TimeUnit timeUnit) {
        Object value = supplier.get();
        set(key, value, expired, timeUnit);
    }

    /**
     * 获取缓存数据
     *
     * @param key 键
     * @return 值
     */
    public static Object get(String key) {
        Data data = CACHE.get(key);
        if (ObjectUtils.isEmpty(data)) {
            return null;
        }
        // 如果过期时间不为空且数据已经过期了 则将数据删除 然后返回空
        if (ObjectUtils.isNotEmpty(data.getExpired()) && System.currentTimeMillis() - data.expired > 0) {
            CACHE.remove(key);
            return null;
        }
        return data.getData();
    }

    /**
     * 获取缓存数据
     *
     * @param key    键
     * @param tClass 值的类型
     * @param <T>    泛型
     * @return 转化类型后的值
     */
    public static <T> T get(String key, Class<T> tClass) {
        Object value = get(key);
        if (ObjectUtils.isNotEmpty(value)) {
            // 通过ObjectMapper进行类型转换
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.convertValue(value, tClass);
        }
        return null;
    }

    /**
     * 获取缓存数据
     *
     * @param key    键
     * @param tClass 值的类型
     * @param <T>    泛型
     * @return 转化类型后的值
     */
    public static <T> T get(String key, Class<T> tClass, Consumer<T> consumer) {
        T value = get(key,tClass);
        if (ObjectUtils.isNotEmpty(value)) {
            // 通过ObjectMapper进行类型转换
            ObjectMapper objectMapper = new ObjectMapper();
            value = objectMapper.convertValue(value, tClass);
        }
        consumer.accept(value);
        return value;
    }

    /**
     * 缓存池中是否存在执行key的缓存
     *
     * @param key key
     * @return 是否存在
     */
    public static Boolean existCache(String key) {
        return CACHE.containsKey(key);
    }

    /**
     * 清除缓存
     *
     * @param key 缓存
     */
    public static void clear(String key) {
        CACHE.remove(key);
    }

    public static class Data {
        /**
         * 过期时间
         */
        private Long expired;
        /**
         * 数据
         */
        private final Object data;

        public Data(Object data) {
            this.data = data;
        }

        public Data(Long expired, Object data) {
            this.data = data;
            this.expired = expired;
        }

        public Object getData() {
            return data;
        }

        public Long getExpired() {
            return expired;
        }
    }

}
